export type Constructor<T = {}> = new (...args: any[]) => T;

export const __REFLECT_KEY__ = "__$IAP_Reflect__"

export function ClassAnnotation (object: object) {
    return function(target: any) {
        if (!target.prototype.hasOwnProperty(__REFLECT_KEY__)) {
            target.prototype[__REFLECT_KEY__] = new ReflectContainer();
        }
        if(!target.prototype[__REFLECT_KEY__].class) {
            target.prototype[__REFLECT_KEY__].class = [];
        }
        target.prototype[__REFLECT_KEY__].class.push(object);
    }
}

export function PropertyAnnotation(object: object) {
    return function (target: any, key: string) {
        if (!target.hasOwnProperty(__REFLECT_KEY__)) {
            target[__REFLECT_KEY__] = new ReflectContainer();
        }
        if(!target[__REFLECT_KEY__].field[key]) {
            target[__REFLECT_KEY__].field[key] = [];
        }
        target[__REFLECT_KEY__].field[key].push(object);
    }
}

export function getFieldAllAnnotation(prototype: any, key: string) :  any[] {
    var c= <ReflectContainer | undefined>prototype[__REFLECT_KEY__]
    if (c && c instanceof ReflectContainer && c.field.hasOwnProperty(key)) {
        return c.field[key];
    }
    else if (prototype.__proto__) {
        return getFieldAllAnnotation(prototype.__proto__, key);
    }
    return [];
}

export function getFieldAnnotation<T>(prototype: any, key: string, constructor: Constructor<T>) : T | undefined {
    var annotations = getFieldAllAnnotation(prototype, key);
    if(annotations && annotations.length > 0) {
        for (let annotation of annotations) {
            if (annotation.constructor == constructor) {
                return annotation;
            }
        }
    }
    return undefined;
}


export function getAllClassAnnotation(prototype: any, annotations: any[] = []) :  any[] {
    var c= <ReflectContainer | undefined>prototype[__REFLECT_KEY__]
    if (c && c instanceof ReflectContainer && c.class.length > 0) {
        annotations.push.apply(annotations, c.class);
    }
    if (prototype.__proto__) {
        getAllClassAnnotation(prototype.__proto__, annotations);
    }
    return annotations;
}

export function getClassAnnotation<T>(prototype: any, constructor: Constructor<T>) : T | undefined {
    return getAllClassAnnotation(prototype).find(t => t instanceof constructor);
}

export function getAllClassAnnotationOnlySelf(prototype: any) : any[] {
    var c= <ReflectContainer | undefined>prototype[__REFLECT_KEY__]
    if (c && c instanceof ReflectContainer) {
        return c.class.slice()
    }
    return [];
}

export function getClassAnnotationOnlySelf<T>(prototype: any, constructor: Constructor<T>) : T | undefined {
    return getAllClassAnnotationOnlySelf(prototype).find(t => t instanceof constructor);
}

export class ReflectContainer {
    class: object[] = [];
    field: {[x: string]: object[]} = {};
} 
